/*
 *  Initiative.cpp
 *  initiativeTracker
 *
 *  Created by Sam Jaffe on 5/9/12.
 *  Copyright 2012 WPI. All rights reserved.
 *
 */

#include <cmath>
#include <iostream>
#include <sstream>
#include <typeinfo>
#include "InitTracker.h"
#include "Initiative.h"

namespace {
    const std::string dash("--------------------\n");
}
    
#pragma mark Constructors
InitTracker::InitTracker() {
    turn = 0;
    next = 0;
}

InitTracker::InitTracker(unsigned short turn, unsigned short next)
: InitTracker("", turn, next) {}

InitTracker::InitTracker(std::string idn) : InitTracker(idn, 0, 0){}

InitTracker::InitTracker(std::string campaignID, unsigned short turn, unsigned short next)
: turn(turn),
next(next),
campaignID(campaignID) {
}

InitTracker::~InitTracker() {}

#pragma mark Getters

void InitTracker::reset() {turn = 0; next = 0;}

unsigned short InitTracker::nextTurn() {
    if ((++next)%charList.size() == 0) {
        std::stringstream ss;
        ss << "\nTurn " << turn++ << " is over, begining turn " << turn;
        std::cout << ss.str() << std::endl;
    }
    checkTraps();
    changeTurn();
    return getTurn();
}

#pragma mark Array Management

std::shared_ptr<Character> InitTracker::get() const {
    return charList[next];
}

std::shared_ptr<Character> InitTracker::get(const int index) const {
    if (index < 0 || index >= charList.size())
        return 0;
    else
        return charList[index];
}

std::shared_ptr<Character> InitTracker::lookup(const int idn) const {
    return *lookup_it(idn);
}

std::vector<std::shared_ptr<Character>>::const_iterator InitTracker::lookup_it(const int idn) const {
    std::vector<std::shared_ptr<Character>>::const_iterator it = charList.begin();
    while (it < charList.end() && (*it++)->getId() != idn);
    return it;
}

std::vector<std::shared_ptr<Character>>::iterator InitTracker::lookup_it(const int idn){
    std::vector<std::shared_ptr<Character>>::iterator it = charList.begin();
    while (it < charList.end() && (*it++)->getId() != idn);
    return it;
}

const int InitTracker::size() const {
    return charList.size();
}

void InitTracker::add(std::shared_ptr<Character> c) {charList.push_back(c);}

void InitTracker::add(std::shared_ptr<Character> c, const int index) {
    std::vector<std::shared_ptr<Character>>::iterator it = charList.begin() + index;
    charList.insert(it, c);
}

void InitTracker::insert(std::shared_ptr<Character> c) {
    std::vector<std::shared_ptr<Character>>::iterator it = charList.begin();
    while (compare(c, *it++));
    charList.insert(it, c);
}

std::vector<std::shared_ptr<Character>>::const_iterator InitTracker::begin() const {
    return charList.begin();
}

std::vector<std::shared_ptr<Character>>::const_iterator InitTracker::end() const {
    return charList.end();
}

std::vector<std::shared_ptr<Character>>::iterator InitTracker::begin() {
    return charList.begin();
}

std::vector<std::shared_ptr<Character>>::iterator InitTracker::end() {
    return charList.end();
}

#pragma mark Other methods
void InitTracker::decode(xercesc::DOMNode* node) {
    insert(std::shared_ptr<Character>(generator->decode(node->getAttributes())));
}

const bool InitTracker::initialize(const std::string idn, unsigned const short turnNum, unsigned const short turnNext) {
    if (campaignID != "") return false;
    
    campaignID = idn;
    turn = turnNum;
    next = turnNext;
    return true;
}

const bool InitTracker::damage(const int idn, const int damage, const bool temp) {
    std::vector<std::shared_ptr<Character>>::iterator it = lookup_it(idn);
    std::shared_ptr<Character> c = (*it);
    std::string type(typeid(*c).name());
    if (damage < 0 && !type.compare(pc_type)) {
        PC* temp_pc = static_pointer_cast<PC>(c);
        if (temp)
            temp_pc->modTemp(damage);
        else
            temp_pc->recover(damage);
    }
    else if (checkHealth(c, damage)) {
        charList.erase(it);
        return true;
    }
    return false;
}

const bool InitTracker::checkHealth(std::shared_ptr<Character> c, const int damage) {
    float bloodied_hp = c->getHp_max()/2;
    unsigned short prev_hp = c->getHp();
    c->modHp(damage);
    unsigned short curr_hp = c->getHp();
    /*
     * Values:
     * -n -> Player has died
     
     * 0 -> No change, target is dying
     * 1 -> Target is now dying
     * 2 -> Target is now dying
     
     * 4 -> Target is no longer dying
     * 5 -> No change, target is bloodied
     * 6 -> Target is now bloodied
     
     * 8 -> Target no longer dying or bloodied
     * 9 -> Target is no longer bloodied
     * 10 -> No change, target is healthy
     */
    short val_code = (ceil(curr_hp/bloodied_hp) * 4) +
                    ceil(prev_hp/bloodied_hp);
    
    if (val_code < 0) {
        std::cout << dash << c->getName() << " has perished!" << dash;
        return false;
    }
    std::string type(typeid(*c).name());
    switch (val_code) {
        case 1: case 2:
            if (!type.compare(pc_type)) {
                std::cout << dash << c->getName() << " has collapsed!" 
                << dash;
                *c << unconscious;
                break;
            }
            std::cout << dash << c->getName() << " has perished!" 
            << dash;
            return false;
        case 4: case 8:
            *c >> unconscious;
            break;
        case 6:
            std::cout << dash << c->getName() << " is bloodied!" 
            << dash;
            break;
        default:
            break;
    }
}

void InitTracker::kill(const int idn) {
    charList.erase(lookup_it(idn));
}

void InitTracker::clear(const bool npc_only) {
    if (npc_only) {
        std::vector<std::shared_ptr<Character>>::iterator it = charList.begin();
        while (it < charList.end()) {
            std::string type(typeid(**it).name());
            if (!type.compare(npc_type))
                charList.erase(it);
            else it++;
        }
    }
    else
        charList.clear();
}

void InitTracker::delay() {
    int size = this->size()-1;
    std::shared_ptr<Character> c1 = charList[next], c2;
    if (next < size) {
        int x = next;
        do {
            c2 = charList[++x];
        } while (c2->isDelayed() && x < size);
        x--;
        
        charList[x] = c1;
        charList[next] = c2;
        
        //changeturn
        
        c1->modHp(c1->getDot());
        c1->checkDelayed();
    }
}

bool InitTracker::changeTurn() {
    
    //bool changeTurn(std::vector<Character*> chars, 
    //                std::vector<std::pair<double,
    //                std::vector<Trap*> > > traps,
    //                const int turn) {
    
    std::shared_ptr<Character> c = get();
    if (true) { //If c doesn't die of a bleed
        //List Ailments
        //Update
    }
    else {
        //remove, rebuild
        changeTurn();
    }
    return true;
}

std::vector<std::shared_ptr<Trap>> InitTracker::checkTraps() {
    //std::vector<Trap*> checkTraps(std::vector<std::pair<double,
    //                              std::vector<Trap*> > > traps, 
    //                              const double turn, const int size) {
    double size = this->size();
    std::vector<std::pair<double, std::vector<std::shared_ptr<Trap>> > > traps;
    
    double lBound = (next-1)/size;
    double uBound = next/size;
    
    std::vector<std::shared_ptr<Trap>> output;
    
    std::vector<std::pair<double, std::vector<std::shared_ptr<Trap>> > >::iterator it;
    std::vector<std::shared_ptr<Trap>>::iterator t;
    
    for (it = traps.begin(); it < traps.end(); it++) {
        if (it->first < uBound && it->first >= lBound) {
            for (t = it->second.begin(); t < it->second.end(); t++)
                output.push_back(*t);
        }
    }
    return output;
}
